﻿Public Module Settings

#Region " Application Constants "
  ''' <summary>
  ''' The path to the running Avian Play application.  Does NOT include trailing slash.
  ''' </summary>
  Public ReadOnly Property ApplicationPath() As String
    Get
      Return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly.GetName.CodeBase)
    End Get
  End Property

  Public ReadOnly Property SQLCECab() As String
    Get
      Return Settings.ApplicationPath & "\sqlce.ppc.wce5.armv4i.CAB"
    End Get
  End Property
#End Region

#Region " INI Paths "
  ' INI settings
  Public ReadOnly Property INIRegExPath() As String
    Get
      Return ApplicationPath & "\INI\RegEx.ini"
    End Get
  End Property

  Public ReadOnly Property INIRegExOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\RegEx.ini"
    End Get
  End Property

  Public ReadOnly Property INILibraryPath() As String
    Get
      Return ApplicationPath & "\INI\Library.ini"
    End Get
  End Property

  Public ReadOnly Property INILibraryOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\Library.ini"
    End Get
  End Property

  Public ReadOnly Property INISettingsPath() As String
    Get
      Return ApplicationPath & "\INI\Settings.ini"
    End Get
  End Property

  Public ReadOnly Property INISettingsOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\Settings.ini"
    End Get
  End Property

  Public ReadOnly Property INISchemaPath() As String
    Get
      Return ApplicationPath & "\INI\Schema.ini"
    End Get
  End Property

  Public ReadOnly Property INISchemaOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\Schema.ini"
    End Get
  End Property

  Public ReadOnly Property INIEventsPath() As String
    Get
      Return ApplicationPath & "\INI\Events.ini"
    End Get
  End Property

  Public ReadOnly Property INIEventsOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\Events.ini"
    End Get
  End Property

  Public ReadOnly Property INIPodcastsPath() As String
    Get
      Return ApplicationPath & "\INI\Podcasts.ini"
    End Get
  End Property

  Public ReadOnly Property INIPodcastsOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\Podcasts.ini"
    End Get
  End Property

  Public ReadOnly Property INIControlsPath() As String
    Get
      Return ApplicationPath & "\INI\Controls.ini"
    End Get
  End Property

  Public ReadOnly Property INIControlsOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\Controls.ini"
    End Get
  End Property

  Public ReadOnly Property INIStatePath() As String
    Get
      Return ApplicationPath & "\INI\State.ini"
    End Get
  End Property

  Public ReadOnly Property INIStateOriginalPath() As String
    Get
      Return ApplicationPath & "\INI\Original\State.ini"
    End Get
  End Property
#End Region

#Region " Now Playing and Podcast Paths "
  Public ReadOnly Property NowPlayingMusicPath() As String
    Get
      Return ApplicationPath & "\NPMusic.apcache"
    End Get
  End Property

  Public ReadOnly Property NowPlayingPodcastPath() As String
    Get
      Return ApplicationPath & "\NPPodcast.apcache"
    End Get
  End Property

  Public ReadOnly Property NowPlayingRadioPath() As String
    Get
      Return ApplicationPath & "\NPRadio.apcache"
    End Get
  End Property

  Public ReadOnly Property EditorPath() As String
    Get
      Return ApplicationPath & "\Editor.apcache"
    End Get
  End Property

  Public ReadOnly Property PodcastDownloadTemporaryPath() As String
    Get
      Return ApplicationPath & "\Download.temp"
    End Get
  End Property
#End Region

#Region " Skin / Language Settings "
  ' Skin settings
  Public ReadOnly Property SkinDefaultPath() As String
    Get
      Return ApplicationPath & "\Skins\Default"
    End Get
  End Property

  Public ReadOnly Property SkinPath() As String
    Get
      Static s As String = Nothing ' Cache the setting, since this should require a restart to take effect

      If s Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("Skin")
          Dim folder As String = .StringValueNoNull("SkinFolder", String.Empty)
          If folder <> String.Empty Then
            Dim folderpath = ApplicationPath & "\Skins\" & folder
            If IO.Directory.Exists(folderpath) Then
              s = folderpath
            End If
          End If
        End With
        If s Is Nothing Then
          s = SkinDefaultPath
        End If
      End If

      Return s
    End Get
  End Property

  Public ReadOnly Property AllSkins() As List(Of String)
    Get
      AllSkins = New List(Of String)
      Try
        For Each d As String In IO.Directory.GetDirectories(ApplicationPath & "\Skins")
          AllSkins.Add(New IO.DirectoryInfo(d).Name)
        Next
      Catch ex As Exception
        ErrorLog.WriteToErrorLog("Settings.AllSkins() :: Exception when enumerating all skin folders :: " & ex.ToString)
      End Try
    End Get
  End Property

  Public ReadOnly Property AllLanguages() As List(Of String)
    Get
      AllLanguages = New List(Of String)
      Try
        For Each d As String In IO.Directory.GetDirectories(ApplicationPath & "\Languages")
          AllLanguages.Add(New IO.DirectoryInfo(d).Name)
        Next
      Catch ex As Exception
        ErrorLog.WriteToErrorLog("Settings.AllLanguages() :: Exception when enumerating all langauge folders :: " & ex.ToString)
      End Try
    End Get
  End Property

#End Region

#Region " Settings.ini Settings "
  Private iniSettingsFile As Ini.File = Nothing
  Private reloadScanPath As Boolean = True
  Private reloadExcludePath As Boolean = True

  Public Sub ReloadSettingsIni()
    ' Reload the settings
    iniSettingsFile = New Ini.File(INISettingsPath)

    ' Load the original settings for this version
    Dim origSettings As New Ini.File(INISettingsOriginalPath)
    Dim changed As Boolean = False

    ' Look for settings that are missing from the user's settings (introduced in a later version while upgrading and preserving user settings, for example)
    For Each s As Ini.Section In origSettings.Sections
      If iniSettingsFile.Section(s.Name) Is Nothing Then
        ' Section doesn't exist, so add it
        iniSettingsFile.AddSection(s.Name)
        changed = True
      End If
      For Each k As String In s.Keys
        Dim ku8 As String = Left(k, 8).ToUpper
        If ku8 <> "SCANPATH" AndAlso ku8 <> "EXCLUDEPATH" AndAlso iniSettingsFile.Section(s.Name).StringValue(k) Is Nothing Then
          ' Setting doesn't exist, so add the default value from the original file
          iniSettingsFile.Section(s.Name).StringValue(k) = s.StringValue(k)
          changed = True
        End If
      Next
    Next

    ' We had some changes, so let's save
    If changed Then
      SaveSettings()
    End If
  End Sub

  Public ReadOnly Property FillMissingArtistOrAlbumArtistTags() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("FillMissingArtistOrAlbumArtistTags")
      End With
    End Get
  End Property

  Public ReadOnly Property FillMissingTagsFromPath() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("FillMissingTagsFromPath")
      End With
    End Get
  End Property

  Public ReadOnly Property OverrideTagsWithTagsFromPath() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("OverrideTagsWithTagsFromPath")
      End With
    End Get
  End Property

  Public ReadOnly Property TagsFromPathRegEx() As String
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .StringValueNoNull("TagsFromPathRegEx")
      End With
    End Get
  End Property

  Public ReadOnly Property LanguageFolder() As String
    Get
      Static s As String = Nothing ' Cache the setting, since this should require a restart to take effect

      If s Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("General")
          Dim folder As String = .StringValueNoNull("LanguageFolder", String.Empty)
          If folder <> String.Empty Then
            Dim folderpath = ApplicationPath & "\Languages\" & folder
            If IO.Directory.Exists(folderpath) Then
              s = folderpath
            End If
          End If
        End With

        If s Is Nothing Then
          s = ApplicationPath & "\Languages\English"
        End If

        s = s.Trim
      End If

      Return s
    End Get
  End Property

  Public ReadOnly Property LanguageFolderDefault() As String
    Get
      Return ApplicationPath & "\Languages\English"
    End Get
  End Property

  Public ReadOnly Property DatabasePath() As String
    Get
      Static s As String = Nothing ' Cache the setting, since this should require a restart to take effect

      If s Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("General")
          s = .StringValueNoNull("DatabasePath")
          s = s.Trim.Replace("<INSTALL>", ApplicationPath)
          If s.Length <= 0 Then
            s = ApplicationPath & "\AvianPlay.sdf"
          End If
        End With
      End If

      Return s
    End Get
  End Property

  Public ReadOnly Property ErrorLogPath() As String
    Get
      Static s As String = Nothing ' Cache the setting, since this should require a restart to take effect

      If s Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("General")
          s = .StringValueNoNull("ErrorLogPath")
          s = s.Trim.Replace("<INSTALL>", ApplicationPath)
          If s.Length <= 0 Then
            s = ApplicationPath & "\Error.log"
          End If
        End With
      End If

      Return s
    End Get
  End Property

  Public ReadOnly Property ErrorLogLengthRows() As Integer
    Get
      Static i As Integer? = Nothing ' Cache the setting, since this should require a restart to take effect

      If i Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("General")
          i = .IntegerValueNoNull("ErrorLogLengthRows", 100)
        End With
      End If

      Return i.Value
    End Get
  End Property

  Public ReadOnly Property SystemIdleTimerResetWhilePlaying() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Return .BooleanValueNoNull("SystemIdleTimerResetWhilePlaying")
      End With
    End Get
  End Property

  Public ReadOnly Property VolumeLogScaling() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Return .BooleanValueNoNull("VolumeLogScaling")
      End With
    End Get
  End Property

  Public ReadOnly Property UnloadOnStop() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Return .BooleanValueNoNull("UnloadOnStop")
      End With
    End Get
  End Property

  Public ReadOnly Property PrebufferFill() As Integer
    Get
      Static i As Integer? = Nothing ' Cache the setting, since this should require a restart to take effect

      If i Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("General")
          i = .IntegerValueNoNull("PrebufferFill", 50)
        End With
      End If

      Return i.Value
    End Get
  End Property

  Public ReadOnly Property BufferLength() As Integer
    Get
      Static i As Integer? = Nothing ' Cache the setting, since this should require a restart to take effect

      If i Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("General")
          i = .IntegerValueNoNull("BufferLen", 500)
        End With
      End If

      Return i.Value
    End Get
  End Property

  Public ReadOnly Property SaveNPCache() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Return .BooleanValueNoNull("SaveNPCache", True)
      End With
    End Get
  End Property

  Public ReadOnly Property UpdateNPCacheAsListUpdated() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Return .BooleanValueNoNull("UpdateNPCacheAsListUpdated", True)
      End With
    End Get
  End Property

  Public ReadOnly Property PowerManagementDebugLogging() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Return .BooleanValueNoNull("PowerManagementDebugLogging", False)
      End With
    End Get
  End Property


  Public ReadOnly Property ShowStreamLargeTitleOnLoad() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Return .BooleanValueNoNull("ShowStreamLargeTitleOnLoad")
      End With
    End Get
  End Property

  Public ReadOnly Property TrackMusicPosition() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Return .BooleanValueNoNull("TrackMusicPosition")
      End With
    End Get
  End Property

  Public ReadOnly Property TrackPodcastPosition() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Return .BooleanValueNoNull("TrackPodcastPosition")
      End With
    End Get
  End Property

  Public ReadOnly Property FailSafePositionTracking() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Return .BooleanValueNoNull("FailSafePositionTracking")
      End With
    End Get
  End Property

  Public ReadOnly Property ShowControlsAfterNowPlayingSelect() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Return .BooleanValueNoNull("ShowControlsAfterNowPlayingSelect")
      End With
    End Get
  End Property

  Public ReadOnly Property SeekPreviewWindow() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Dim val As Integer = .IntegerValueNoNull("SeekPreviewWindow", 500)
        If val < 100 Then val = 100
        Return val
      End With
    End Get
  End Property

  Public ReadOnly Property PreviousButtonResetsTrackTimerAfter() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Playback")
        Return .IntegerValueNoNull("PreviousButtonResetsTrackTimerAfter", 2)
      End With
    End Get
  End Property

  Public ReadOnly Property ParseStreamTitlesUsingRegEx() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Streams")
        Return .BooleanValueNoNull("ParseStreamTitlesUsingRegEx", True)
      End With
    End Get
  End Property

  Public ReadOnly Property UseDefaultRegExPatternOnMatchFailure() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Streams")
        Return .BooleanValueNoNull("UseDefaultRegExPatternOnMatchFailure", True)
      End With
    End Get
  End Property

  Public ReadOnly Property AlbumArtListDisplayMode() As Playlists.AVPLPlaylistEntry.AlbumArtListDisplayModeEnum
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Display")
        Return .IntegerValueNoNull("AlbumArtListDisplayMode")
      End With
    End Get
  End Property

  Public ReadOnly Property AlbumArtListCache() As Integer
    Get
      Static i As Integer? = Nothing ' Cache the setting, since this should require a restart to take effect

      If i Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("Display")
          i = .IntegerValueNoNull("AlbumArtListCache")
        End With
      End If

      Return i.Value
    End Get
  End Property

  Public ReadOnly Property HowToShowButtonTimeArguments() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Display")
        Return .IntegerValueNoNull("HowToShowButtonTimeArguments", 2)
      End With
    End Get
  End Property

  Public ReadOnly Property TabMusicVisible() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      If Not TabPodcastsVisible AndAlso Not TabRadioVisible Then
        Return True
      Else
        With iniSettingsFile.SectionNoNull("Display")
          Return .BooleanValueNoNull("TabMusicVisible", True)
        End With
      End If
    End Get
  End Property

  Public ReadOnly Property TabPodcastsVisible() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Display")
        Return .BooleanValueNoNull("TabPodcastsVisible", True)
      End With
    End Get
  End Property

  Public ReadOnly Property TabRadioVisible() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Display")
        Return .BooleanValueNoNull("TabRadioVisible", True)
      End With
    End Get
  End Property

  Public ReadOnly Property LibraryScanPaths() As List(Of String)
    Get
      Static ret As List(Of String) = Nothing ' Cache the setting, since this should require a restart to take effect (and also for performance)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      If ret Is Nothing OrElse reloadScanPath Then
        ret = New List(Of String)
        With iniSettingsFile.SectionNoNull("Library")
          For Each k As String In .Keys
            If Left(k, 8).ToUpper = "SCANPATH" Then
              Dim v As String = .StringValue(k)
              If v IsNot Nothing Then
                If v.Length > 1 AndAlso v.EndsWith("\") Then
                  v = v.Substring(0, v.Length - 1)
                End If
                If v.Length > 0 Then
                  ret.Add(v)
                End If
              End If
            End If
          Next
        End With
        reloadScanPath = False
      End If
      Return ret
    End Get
  End Property

  Public ReadOnly Property LibraryExcludePaths() As List(Of String)
    Get
      Static ret As List(Of String) = Nothing ' Cache the setting, since this should require a restart to take effect (and also for performance)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      If ret Is Nothing OrElse reloadExcludePath Then
        ret = New List(Of String)
        With iniSettingsFile.SectionNoNull("Library")
          For Each k As String In .Keys
            If Left(k, 11).ToUpper = "EXCLUDEPATH" Then
              Dim v As String = .StringValue(k)
              If v IsNot Nothing Then
                If v.Length > 1 AndAlso v.EndsWith("\") Then
                  v = v.Substring(0, v.Length - 1)
                End If
                If v.Length > 0 Then
                  ret.Add(v)
                End If
              End If
            End If
          Next
        End With
        reloadExcludePath = False
      End If
      Return ret
    End Get
  End Property

  Public Sub RefreshScanAndExcludePathsOnNextPropertyGet()
    reloadScanPath = True
    reloadExcludePath = True
  End Sub


  Public ReadOnly Property ShowDetailedLibraryMonitorMessages() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("ShowDetailedLibraryMonitorMessages")
      End With
    End Get
  End Property

  Public ReadOnly Property CalculateMD5Hashes() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("CalculateMD5Hashes", True)
      End With
    End Get
  End Property

  Public ReadOnly Property PerformFullScanOnStartup() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("PerformFullScanOnStartup", True)
      End With
    End Get
  End Property

  Public ReadOnly Property MonitorScannedPathsRealtime() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .BooleanValueNoNull("MonitorScannedPathsRealtime", True)
      End With
    End Get
  End Property

  Public ReadOnly Property RefreshItemListAfter() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Library")
        Return .IntegerValueNoNull("RefreshItemListAfter", 15)
      End With
    End Get
  End Property

  Public ReadOnly Property RefreshStreamURLsFromPLSAfter() As Integer
    Get
      Static i As Integer? = Nothing ' Cache the setting, since this should require a restart to take effect

      If i Is Nothing Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("Streams")
          i = .IntegerValueNoNull("RefreshStreamURLsFromPLSAfter", 14)
        End With
      End If

      Return i.Value
    End Get
  End Property

  Public ReadOnly Property MemorizedSongsPath() As String
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("General")
        Dim s As String = .StringValueNoNull("MemorizedSongsPath")
        s = s.Trim.Replace("<INSTALL>", ApplicationPath)
        If s.Length > 0 Then
          Return s
        End If
      End With
      Return "\My Documents\Avian Play Memorized Songs.txt"
    End Get
  End Property

  Public ReadOnly Property LongPressTime() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Control")
        Return .IntegerValueNoNull("LongPressTime", 750)
      End With
    End Get
  End Property

  Public ReadOnly Property RewTimerInterval() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Control")
        Return .IntegerValueNoNull("RewTimerInterval", 100)
      End With
    End Get
  End Property

  Public ReadOnly Property FwdTimerInterval() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Control")
        Return .IntegerValueNoNull("FwdTimerInterval", 100)
      End With
    End Get
  End Property

  Public ReadOnly Property RewSecondsPerTick() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Control")
        Return .IntegerValueNoNull("RewSecondsPerTick", 2)
      End With
    End Get
  End Property

  Public ReadOnly Property FwdSecondsPerTick() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Control")
        Return .IntegerValueNoNull("FwdSecondsPerTick", 2)
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSubscriptionInterval() As Integer
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .IntegerValueNoNull("PodcastSubscriptionInterval")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInFilePath() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInFilePath")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInArtist() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInArtist")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInAlbumArtist() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInAlbumArtist")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInTitle() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInTitle")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInAlbum() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInAlbum")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInGenre() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInGenre")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchInComments() As Boolean
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      With iniSettingsFile.SectionNoNull("Podcasts")
        Return .BooleanValueNoNull("SearchInComments")
      End With
    End Get
  End Property

  Public ReadOnly Property PodcastSearchTerms() As String()
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      Return ValueListString("Podcasts", "SearchTerm", vbCr).Split(vbCr)
    End Get
  End Property



  Public ReadOnly Property GetSettingsSchema() As Ini.Section
    Get
      Static schema As New Ini.File(INISchemaPath)
      Return schema.SectionNoNull("Settings.ini")
    End Get
  End Property

  Public Property ValueString(ByVal section As String, ByVal iniKey As String) As String
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      Return iniSettingsFile.SectionNoNull(section).StringValue(iniKey)
    End Get
    Set(ByVal value As String)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      iniSettingsFile.SectionNoNull(section).StringValue(iniKey) = value
    End Set
  End Property

  Public Property ValueListString(ByVal section As String, ByVal iniKey As String, ByVal delimiter As Char) As String
    Get
      ValueListString = ""
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()

      With iniSettingsFile.SectionNoNull(section)
        Dim i As Integer = 1
        Dim str As String = Settings.ValueString(section, iniKey & i.ToString)
        While str IsNot Nothing
          If i > 1 Then
            ValueListString &= delimiter
          End If
          ValueListString &= str
          i += 1
          str = Settings.ValueString(section, iniKey & i.ToString)
        End While
      End With
    End Get
    Set(ByVal value As String)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()

      ' Remove all existing keys associated with this ValueList
      Dim i As Integer = 1
      Dim removeList As New List(Of String)
      Dim curkey As String = iniKey & i.ToString
      Dim str As String = Settings.ValueString(section, curkey)
      While str IsNot Nothing
        removeList.Add(curkey)
        i += 1
        curkey = iniKey & i.ToString
        str = Settings.ValueString(section, curkey)
      End While

      For Each key As String In removeList
        iniSettingsFile.Section(section).Remove(key)
      Next

      ' Add new keys, incrementing counter for each value in the virtual list
      i = 1
      Dim strs() As String = value.Split(delimiter)
      For Each s As String In strs
        s = s.Trim
        If s.Length > 0 Then
          iniSettingsFile.Section(section).Add(iniKey & i.ToString, s)
          i += 1
        End If
      Next
    End Set
  End Property

  Public Property ValueInteger(ByVal section As String, ByVal iniKey As String) As Integer?
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      Return iniSettingsFile.SectionNoNull(section).IntegerValue(iniKey)
    End Get
    Set(ByVal value As Integer?)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      iniSettingsFile.SectionNoNull(section).IntegerValue(iniKey) = value
    End Set
  End Property

  Public Property ValueLong(ByVal section As String, ByVal iniKey As String) As Long?
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      Return iniSettingsFile.SectionNoNull(section).LongValue(iniKey)
    End Get
    Set(ByVal value As Long?)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      iniSettingsFile.SectionNoNull(section).LongValue(iniKey) = value
    End Set
  End Property

  Public Property ValueBoolean(ByVal section As String, ByVal iniKey As String) As Boolean?
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      Return iniSettingsFile.SectionNoNull(section).BooleanValue(iniKey)
    End Get
    Set(ByVal value As Boolean?)
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      iniSettingsFile.SectionNoNull(section).BooleanValue(iniKey) = value
    End Set
  End Property

  Public Sub SaveSettings()
    If iniSettingsFile IsNot Nothing Then
      iniSettingsFile.Save(INISettingsPath, True)
    End If
  End Sub

#End Region

#Region " Extensions / MIME Maps "
  Public Class AudioExtensionMap
    Public Extension As String
    Public Type As AudioFileType
    Public ReadOnly Property FileSpec() As String
      Get
        Static fs As String = "*." & Extension
        Return fs
      End Get
    End Property
    Public Sub New()
    End Sub
    Public Sub New(ByVal ext As String, ByVal tp As AudioFileType)
      Extension = ext
      Type = tp
    End Sub
  End Class

  Public Class PlaylistExtensionMap
    Public Extension As String
    Public Type As PlaylistFileType
    Public ReadOnly Property FileSpec() As String
      Get
        Static fs As String = "*." & Extension
        Return fs
      End Get
    End Property
    Public Sub New()
    End Sub
    Public Sub New(ByVal ext As String, ByVal tp As PlaylistFileType)
      Extension = ext
      Type = tp
    End Sub
  End Class

  Public Class AudioMimeMap
    Public Mime As String
    Public Type As AudioFileType
    Public Sub New()
    End Sub
    Public Sub New(ByVal mm As String, ByVal tp As AudioFileType)
      Mime = mm
      Type = tp
    End Sub
  End Class

  Public Enum AudioFileType
    WAV
    MP3
    OGG
    WMA
    FLAC
  End Enum

  Public Enum PlaylistFileType
    AVPL
    M3U
    PLS
  End Enum

  Private Function ParseExtensionMapValue(ByVal str As String) As List(Of String)
    ParseExtensionMapValue = New List(Of String)
    Dim strs() As String = str.Split(",")
    For Each s As String In strs
      s = s.Trim
      If s.Length > 0 Then
        ParseExtensionMapValue.Add(s)
      End If
    Next
  End Function

  Private Function ParseMimeMapValue(ByVal str As String) As List(Of String)
    ParseMimeMapValue = New List(Of String)
    Dim strs() As String = str.Split(";")
    For Each s As String In strs
      s = s.Trim
      If s.Length > 0 Then
        ParseMimeMapValue.Add(s)
      End If
    Next
  End Function

  Public ReadOnly Property GetAudioExtensionMaps() As List(Of AudioExtensionMap)
    Get
      Static aem As New List(Of AudioExtensionMap)

      If aem.Count <= 0 Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("ExtensionMap")
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("MP3_Extensions"))
            aem.Add(New AudioExtensionMap(ext, AudioFileType.MP3))
          Next
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("OGG_Extensions"))
            aem.Add(New AudioExtensionMap(ext, AudioFileType.OGG))
          Next
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("WMA_Extensions"))
            aem.Add(New AudioExtensionMap(ext, AudioFileType.WMA))
          Next
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("FLAC_Extensions"))
            aem.Add(New AudioExtensionMap(ext, AudioFileType.FLAC))
          Next
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("WAV_Extensions"))
            aem.Add(New AudioExtensionMap(ext, AudioFileType.WAV))
          Next
        End With
      End If

      Return aem
    End Get
  End Property

  Public ReadOnly Property GetAudioExtensionMapForFile(ByVal filename As String) As AudioExtensionMap
    Get
      Dim ext As String = System.IO.Path.GetExtension(filename)

      If ext IsNot Nothing Then
        ext = ext.Replace(".", "").Trim ' Get rid of the 'dot' and trim the string, just in case

        If ext.Length > 0 Then
          For Each aem In GetAudioExtensionMaps
            If String.Compare(aem.Extension, ext, True) = 0 Then
              ' Return a copy of the extension map
              Return New AudioExtensionMap(aem.Extension, aem.Type)
            End If
          Next
        End If
      End If

      Return Nothing
    End Get
  End Property


  Public ReadOnly Property GetPlaylistExtensionMaps() As List(Of PlaylistExtensionMap)
    Get
      Static pem As New List(Of PlaylistExtensionMap)

      If pem.Count <= 0 Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("ExtensionMap")
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("AVPL_Extensions"))
            pem.Add(New PlaylistExtensionMap(ext, PlaylistFileType.AVPL))
          Next
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("M3U_Extensions"))
            pem.Add(New PlaylistExtensionMap(ext, PlaylistFileType.M3U))
          Next
          For Each ext As String In ParseExtensionMapValue(.StringValueNoNull("PLS_Extensions"))
            pem.Add(New PlaylistExtensionMap(ext, PlaylistFileType.PLS))
          Next
        End With
      End If

      Return pem
    End Get
  End Property

  Public ReadOnly Property GetPlaylistExtensionMapForFile(ByVal filename As String) As PlaylistExtensionMap
    Get
      Dim ext As String = System.IO.Path.GetExtension(filename)

      If ext IsNot Nothing Then
        ext = ext.Replace(".", "").Trim ' Get rid of the 'dot' and trim the string, just in case

        If ext.Length > 0 Then
          For Each pem In GetPlaylistExtensionMaps
            If String.Compare(pem.Extension, ext, True) = 0 Then
              ' Return a copy of the extension map
              Return New PlaylistExtensionMap(pem.Extension, pem.Type)
            End If
          Next
        End If
      End If

      Return Nothing
    End Get
  End Property

  Public ReadOnly Property GetAudioMimeMaps() As List(Of AudioMimeMap)
    Get
      Static amm As New List(Of AudioMimeMap)

      If amm.Count <= 0 Then
        If iniSettingsFile Is Nothing Then ReloadSettingsIni()
        With iniSettingsFile.SectionNoNull("MimeMap")
          For Each mime As String In ParseMimeMapValue(.StringValueNoNull("mp3"))
            amm.Add(New AudioMimeMap(mime, AudioFileType.MP3))
          Next
          For Each mime As String In ParseMimeMapValue(.StringValueNoNull("ogg"))
            amm.Add(New AudioMimeMap(mime, AudioFileType.OGG))
          Next
          For Each mime As String In ParseMimeMapValue(.StringValueNoNull("wma"))
            amm.Add(New AudioMimeMap(mime, AudioFileType.WMA))
          Next
          For Each mime As String In ParseMimeMapValue(.StringValueNoNull("flac"))
            amm.Add(New AudioMimeMap(mime, AudioFileType.FLAC))
          Next
          For Each mime As String In ParseMimeMapValue(.StringValueNoNull("wav"))
            amm.Add(New AudioMimeMap(mime, AudioFileType.WAV))
          Next
        End With
      End If

      Return amm
    End Get
  End Property

  Public ReadOnly Property GetAudioExtensionForMimeType(ByVal mime As String) As String
    Get
      Dim mm As String = mime.Trim

      If mm.Length > 0 Then
        For Each amm As AudioMimeMap In GetAudioMimeMaps
          If String.Compare(amm.Mime, mm, True) = 0 Then
            Select Case amm.Type
              Case AudioFileType.FLAC
                Return ".flac"
              Case AudioFileType.MP3
                Return ".mp3"
              Case AudioFileType.OGG
                Return ".ogg"
              Case AudioFileType.WAV
                Return ".wav"
              Case AudioFileType.WMA
                Return ".wma"
            End Select
          End If
        Next
      End If

      Return Nothing
    End Get
  End Property

  Public ReadOnly Property GetSetting(ByVal section As String, ByVal key As String) As String
    Get
      If iniSettingsFile Is Nothing Then ReloadSettingsIni()
      If iniSettingsFile.Section(section) IsNot Nothing Then
        Return iniSettingsFile.Section(section).StringValue(key)
      End If
      Return Nothing
    End Get
  End Property

#End Region

#Region " State.ini "
  Public ReadOnly Property GetStateINI() As Ini.File
    Get
      Return New Ini.File(INIStatePath)
    End Get
  End Property
#End Region

End Module
